/*
 * Copyright 2020 lcnap
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package pers.lcnap.simplerpc.util;

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import io.netty.buffer.ByteBuf;
import pers.lcnap.simplerpc.rpc.RpcRuntimeException;

import java.util.Arrays;

/**
 * Kryo 序列化工具类
 */
public class KryoUtil {

    private static int bufferSize = 512;

    private static final ThreadLocal<Kryo> local = ThreadLocal.withInitial(() -> {
        Kryo kryo = new Kryo();
        kryo.setRegistrationRequired(false);
        return kryo;
    });

    public static Kryo getKryo() {
        return local.get();
    }

    /**
     * 将对象序列化成二进制
     *
     * @param object 要序列化对象
     * @param <T>
     * @return
     */
    public static <T> byte[] write(T object) {

        try (Output output = new Output(bufferSize)) {
            getKryo().writeClassAndObject(output, object);
            return output.toBytes();
        }
    }

    /**
     * 将对象序列化，并写入 byteBuf.
     *
     * @param byteBuf 用于写入序列化数据
     * @param object  要序列化的对象
     * @return 序列数据长度
     */
    public static int write(ByteBuf byteBuf, Object object) {
        try (Output output = new Output(bufferSize)) {
            getKryo().writeClassAndObject(output, object);
            byte[] buffer = output.getBuffer();
            byteBuf.writeBytes(buffer);
            return buffer.length;
        }
    }

    /**
     * 反序列成对象
     *
     * @param byteBuf 数据源
     * @param <T>     对象
     * @return
     */
    public static <T> T read(ByteBuf byteBuf) {
        byte[] bytes = new byte[byteBuf.readableBytes()];
        byteBuf.readBytes(bytes);
        try (Input input = new Input(bytes)) {
            Object o = getKryo().readClassAndObject(input);
            return (T) o;
        }
    }

    public static int getBufferSize() {
        return bufferSize;
    }

    public static void setBufferSize(int bufferSize) {
        KryoUtil.bufferSize = bufferSize;
    }

    public static void main(String[] args) {
        try (Output output = new Output(100)) {
            getKryo().writeClassAndObject(output, "String.class");
            byte[] buffer = output.toBytes();
            System.out.println(Arrays.toString(buffer));
            System.out.println(buffer.length);
            Input input = new Input(buffer);
            String s = (String) getKryo().readClassAndObject(input);
            System.out.println(s);

        }

        byte[] write = write(new RpcRuntimeException());
        System.out.println(write.length);

    }

}
